// Copyright (c) 2022 Presto Labs Pte. Ltd.
// Author: hgpark

#pragma once

#include <glog/logging.h>
#include <spdlog/spdlog.h>

#include <algorithm>
#include <cmath>
#include <functional>
#include <map>
#include <memory>
#include <set>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>

#include "coin/proto/coin_order_enums.pb.h"
#include "coin2/app/driver.h"
#include "coin2/base/log.h"
#include "coin2/exchange/base/feed/update.h"
#include "coin2/strategy/order_executor/executor.h"
#include "coin2/exchange/risk/risk_manager.h"
#include "coin2/strategy/rmm_strat/rmm_strat_config.pb.h"
#include "coin2/strategy/strategy.h"
#include "coin2/strategy/util/config_reporter.h"
#include "presto/quant/base/time.h"

namespace coin2::strategy::rmm_strat {

using ::coin::proto::OrderDirection;
using ::coin::proto::OrderDuration;
using ::coin::proto::OrderEvent;
using ::coin::proto::OrderState;
using ::coin::proto::OrderType;
using ::coin2::app::impl::Driver;
using ::coin2::exchange::base::feed::FeedUpdate;
using ::coin2::exchange::base::market::ExchangeType;
using ::coin2::exchange::base::market::MarketExchangeApi;
using ::coin2::exchange::base::order::IOrderSubsystem;
using ::coin2::exchange::base::order::OrderUpdate;
using ::coin2::exchange::base::symbology::IProductHolder;
using ::coin2::exchange::base::order::IOrderContext;
using ::coin2::exchange::order::RiskManagerConfig;
using ::coin2::exchange::risk::RiskManager;
using ::coin2::strategy::order_executor::OrderExecutorSystem;

class GenOiSubStrategy;

class GenOiStrategy : public ::coin2::strategy::IStrategy {
 public:
  GenOiStrategy(const std::experimental::filesystem::path& strat_json);
  void Init(::coin2::app::Driver* driver) override;
  void onBookFeed(const FeedUpdate& upd) override;
  void onOpenInterestFeed(const FeedUpdate& upd) override;
  void onAccountInfo(const OrderUpdate& upd) override;
  void onOrderLog(const coin::proto::OrderGatewayLog& order_log) override;
  void onOrderSubsystemError(const OrderUpdate&) override;
  void onCleanup(Driver* driver) override;
  const GenOiProductConfig& CommonConfig() const { return common_config_; }
  std::string Owner0() const { return owner0_; }
  std::string Owner1() const { return owner1_; }

 private:
  int64_t strategy_created_time_{0L};
  int64_t position_log_ts_{0L};
  GenOiProductConfig common_config_;
  OrderExecutorSystem* oe0_ = nullptr;
  OrderExecutorSystem* oe1_ = nullptr;
  std::vector<std::unique_ptr<GenOiSubStrategy>> genoi_sub_strats_;
  std::unordered_map<std::string, std::vector<GenOiSubStrategy*>> feed_sub_map_;
  std::unordered_map<std::string, GenOiSubStrategy*> order_sub_map_;
  std::unique_ptr<RiskManager> risk_manager_;
  std::unique_ptr<StrategyReporter2> strat_reporter_;
  std::string owner0_, owner1_;
};

class GenOiSubStrategy
    : public ::coin2::strategy::IStrategy {
 public:
  GenOiSubStrategy(GenOiStrategy* parent,
                   const GenOiProductConfig& config,
                   int64_t strategy_created_time);
  void Init(::coin2::app::Driver* driver,
            exchange::base::strategy_util::StrategyLogger* strat_logger);
  void SetRiskManager(RiskManager* risk_manager) { risk_manager_ = risk_manager; }
  void onBookFeed(const std::string& product_key, const FeedUpdate& upd);
  void onOpenInterestFeed(const FeedUpdate& upd) override;
  void onAccountInfo(const OrderUpdate& upd) final;
  void SubmitOrders(
      const double position,
      const double another_position,
      const double target_position,
      int64_t* prev_post_ts,
      ::coin2::strategy::order_executor::OrderExecutorSystem* oe,
      int64_t timestamp);
  void ApproachTargetPosition(
      ::coin2::strategy::order_executor::OrderExecutorSystem* oe0,
      ::coin2::strategy::order_executor::OrderExecutorSystem* oe1,
      int64_t timestamp,
      bool print_log);
  const IProduct* TradeProduct() const {return trade_product_.get(); }
  const IProductHolder* TradeProductHolder() const { return trade_product_holder_.get(); }
  MarketExchangeApi Mea() {
    return MarketExchangeApi::FromString(config_.trade_product().mea());
  }
  IOrderSubsystem* os() { return driver_->order()->GetUniqueSystem(Mea()); }

 private:
  std::unique_ptr<IProduct> trade_product_;
  std::unique_ptr<IProductHolder> trade_product_holder_;
  GenOiProductConfig config_;
  RiskManager* risk_manager_;
  bool enable_telemetry_{false};
  int64_t strategy_created_time_{0L};
  int64_t book_ts_{0L};
  int64_t main_oi_ts_{0L}, aux_oi_ts_{0L};
  int64_t prev_post_ts0_{0L}, prev_post_ts1_{0L};
  int64_t last_oi_ratio_ts_{0L};
  std::optional<double> midp_;
  std::optional<double> main_ref_multiplier_, aux_ref_multiplier_;
  std::optional<double> main_ref_name_multiplier_, aux_ref_name_multiplier_;
  std::optional<double> position0_, position1_;
  std::optional<double> main_ref_oi_, aux_ref_oi_;
  std::optional<double> main_aux_oi_ratio_;
  MarketExchangeApi main_ref_mea_, aux_ref_mea_;
  Driver* driver_;
  exchange::base::strategy_util::StrategyLogger* strat_logger_;
  GenOiStrategy* parent_{nullptr};
  std::set<std::string> feed_keys_;
};

}  // namespace coin2::strategy::genoi_strat

